/*
 * LiveRelayServer.cpp
 *
 *  Created on: 2016年4月30日
 *      Author: terry
 */

#include "LiveRelayServer.h"
#include "AppProperties.h"
#include "CLog.h"
#include "MediaControlStanza.h"
#include "Socket.h"
#include "TStringUtil.h"
#include "RtpMediaFormat.h"
#include "XmppUtil.h"

static const int SETUP_DURATION = 1000 * 10;


static void LocalRtspCasterEventCallback(const RtspCasterEvent* event, void* context)
{
    LiveRelayServer* obj = (LiveRelayServer*)context;
    obj->onRtspCasterEvent(event);
}


LiveRelayServer::LiveRelayServer():
    m_setupReady(false)
{
    
}

LiveRelayServer::~LiveRelayServer()
{
    //caster_set_event_callback(NULL, NULL);

    
}


bool LiveRelayServer::start()
{
	if (!isRunning())
	{
		int port = AppProperties::getProperties().getInt("Rtsp.Port", 1554);

		relay_init(port);
		caster_set_event_callback(LocalRtspCasterEventCallback, this);
	
		openClient();

		comn::Thread::start();
	}
    return true;
}

void LiveRelayServer::stop()
{
	if (isRunning())
	{
		comn::Thread::stop();
		
        caster_set_event_callback(NULL, NULL);
		relay_quit();
	}

	closeClient();
}

void LiveRelayServer::clientConnected(const std::string& jid)
{
	std::string name = comn::StringUtil::getHead(jid, '@');
    m_nameMap.put(jid, name);

    queryMediaFormat(jid);
}

void LiveRelayServer::clientDisconnected(const std::string& jid)
{
    m_nameMap.remove(jid);
}

void LiveRelayServer::onRtspCasterEvent(const RtspCasterEvent* event)
{
    if (event->type == CASTER_SESSION_REQUEST)
    {
        CLog::debug("request rtsp session: %s\n", event->name);

        std::string devName;
        std::string chlName;
        parseStreamName(event->name, devName, chlName);

        std::string jid = comn::StringUtil::format("%s@%s/Device", devName.c_str(), getDomain().c_str());
        //if (m_nameMap.findByValue(event->name, jid))
        {
            MFormat fmt;
            memset(&fmt, 0, sizeof(fmt));
            fmt.codec = MCODEC_RAW;

            HANDLE handle = NULL;
            int protocol = getTransportProtocol();
            int rc = relay_chl_open(&handle, protocol, event->name, &fmt);

            static const int MAX_URL = 2048;
            char vBuffer[MAX_URL];
            memset(vBuffer, 0, sizeof(vBuffer));
            char aBuffer[MAX_URL];
            memset(aBuffer, 0, sizeof(aBuffer));
            relay_chl_get_transport(handle, vBuffer, MAX_URL, aBuffer, MAX_URL);

            std::string videoUrl = replaceIP(vBuffer);
            std::string audioUrl = replaceIP(aBuffer);

            m_setupReady = false;
            setupStream(jid, videoUrl, audioUrl, chlName);

            m_event.timedwait(SETUP_DURATION);

            if (m_setupReady)
            {
                CLog::info("LiveRelayServer setup is ready. %s\n", event->name);
            }
            else
            {
                CLog::warning("LiveRelayServer setup failed. %s\n", event->name);
                relay_chl_close(handle);
            }
        }
        //else
        {
            //CLog::info("device is NOT online.\n");
        }
    }
    else if (event->type == CASTER_SESSION_CREATE)
    {
        CLog::debug("create session: %s\n", event->name);
    }
    else if (event->type == CASTER_SESSION_DESTROY)
    {
        CLog::debug("destroy session: %s\n", event->name);

        HANDLE handle = relay_chl_find(event->name);
        if (handle != NULL)
        {
            relay_chl_close(handle);
        }

        std::string devName;
        std::string chlName;
        parseStreamName(event->name, devName, chlName);

        std::string jid = comn::StringUtil::format("%s@%s/Device", devName.c_str(), getDomain().c_str());
        teardownStream(jid, chlName);
    }
}

void LiveRelayServer::queryMediaFormat(const std::string& jid)
{
    MediaControlStanza* stanza = new MediaControlStanza(NULL);
	stanza->setCommand(MediaControlStanza::COMMAND_DESC);

	gloox::IQ req(IQ::Set, jid);
	req.addExtension(stanza);
    
    m_client->send(req, this, 0);
}

void LiveRelayServer::setupStream(const std::string& jid, const std::string& videoUrl, const std::string& audioUrl, const std::string& chl)
{
	MediaControlStanza* stanza = new MediaControlStanza(NULL);
	stanza->setCommand(MediaControlStanza::COMMAND_SETUP);
	stanza->setUrl(videoUrl, audioUrl);
    stanza->setChannel(chl);

	gloox::IQ req(IQ::Set, jid);
	req.addExtension(stanza);

    m_client->send(req, this, 0);
}

void LiveRelayServer::teardownStream(const std::string& jid, const std::string& chl)
{
    MediaControlStanza* stanza = new MediaControlStanza(NULL);
    stanza->setCommand(MediaControlStanza::COMMAND_TEARDOWN);
    stanza->setUrl("rtp://video", "rtp://audio");
    stanza->setChannel(chl);

    gloox::IQ req(IQ::Set, jid);
    req.addExtension(stanza);

    m_client->send(req, this, 0);
}

void LiveRelayServer::onCommandDesc(const JID& jid, IQ::IqType subtype, const MediaControlStanza* stanza)
{
	// pass
}

void LiveRelayServer::onCommandSetup(const JID& jid, IQ::IqType subtype, const MediaControlStanza* stanza)
{
    CLog::debug("LiveRelayServer::onCommandSetup. subtype:%d %s, jid:%s\n",
		subtype, toString(subtype), jid.full().c_str());

    if (subtype == IQ::Result)
    {
        std::string dev = jid.username();
        std::string chl = stanza->getChannel();
        std::string name = makeStreamName(dev, chl);

        std::string videoUrl;
        std::string audioUrl;
        stanza->getUrl(videoUrl, audioUrl);

        RtpMediaFormat rtpFormat;
        rtpFormat.parse(videoUrl);

        HANDLE handle = relay_chl_find(name.c_str());
        if (handle != NULL)
        {
            relay_chl_set_source(handle, videoUrl.c_str(), audioUrl.c_str());
        }
        m_setupReady = true;
    }
    else
    {
        m_setupReady = false;
    }
    m_event.post();
}

void LiveRelayServer::onCommandTeardown(const JID& jid, IQ::IqType subtype, const MediaControlStanza* stanza)
{
	// pass
}

std::string LiveRelayServer::replaceIP(const std::string& url)
{
    std::string domain = AppProperties::getProperties().getString("App.Host", "");
    if (domain.empty())
    {
        domain = comn::SockAddr::getLocalIP();
    }

    std::string old("0.0.0.0");
    std::string newUrl(url);
    size_t pos = newUrl.find(old);
    if (pos != std::string::npos)
    {
    	newUrl.replace(pos, old.size(), domain);
    }

    return newUrl;
}

std::string LiveRelayServer::getDomain()
{
	std::string domain = AppProperties::getProperties().getString("Xmpp.Server", "");
	if (domain.empty())
	{
		domain = comn::SockAddr::getLocalIP();
	}
	return domain;
}

int LiveRelayServer::run()
{
	while (!m_canExit)
	{
		int timeout = 1000 * 1000;
		ConnectionError ce = m_client->recv(timeout);
		if (ce == ConnNoError)
		{
			/// pass
		}
		else
		{
            m_event.timedwait(1000 * 10);

			m_client->connect(false);
			CLog::info("reconnect.\n");
		}
	}
	return 0;
}

void LiveRelayServer::doStop()
{
	m_event.post();
}

bool LiveRelayServer::startup()
{
	return true;
}

void LiveRelayServer::cleanup()
{
	// pass
}

bool LiveRelayServer::handleIq( const IQ& iq )
{
	const MediaControlStanza* stanza = iq.findExtension<MediaControlStanza>(MediaControlStanza::EXTENSION_TYPE);
	if (!stanza)
	{
		return false;
	}

	const JID& jid = iq.to();

	// = new MediaControlStanza(iq.tag());
	std::string cmd = stanza->getCommand();
	if (cmd == "desc")
	{
		onCommandDesc(jid, iq.subtype(), stanza);
		return true;
	}
	else if (cmd == "setup")
	{
		onCommandSetup(jid, iq.subtype(), stanza);
		return true;
	}
	else if (cmd == "play")
	{
	}
	else if (cmd == "teardown")
	{
		onCommandTeardown(jid, iq.subtype(), stanza);
		return true;
	}

	return false;
}

void LiveRelayServer::handleIqID( const IQ& iq, int context )
{
	const MediaControlStanza* stanza = iq.findExtension<MediaControlStanza>(MediaControlStanza::EXTENSION_TYPE);
	if (!stanza)
	{
		return;
	}

    const JID& jid = iq.from();


    // = new MediaControlStanza(iq.tag());
    std::string cmd = stanza->getCommand();
    if (cmd == "desc")
    {
        onCommandDesc(jid, iq.subtype(), stanza);
    }
    else if (cmd == "setup")
    {
        onCommandSetup(jid, iq.subtype(), stanza);
    }
    else if (cmd == "play")
    {
    }
    else if (cmd == "teardown")
    {
        onCommandTeardown(jid, iq.subtype(), stanza);
    }
}

void LiveRelayServer::onConnect()
{
	std::string jid = getJid();
	CLog::info("connected to server. %s\n", jid.c_str());
}

void LiveRelayServer::onDisconnect( ConnectionError e )
{
	CLog::warning("disconnected from server.\n");
}

bool LiveRelayServer::onTLSConnect( const CertInfo& info )
{
	CLog::info("LiveRelayServer::onTLSConnect.\n");

	return true;
}

std::string LiveRelayServer::getJid() const
{
	comn::IniProperties& props = AppProperties::getProperties();

	std::string server = props.getString("Xmpp.Server");
	std::string user = props.getString("Xmpp.User");
	std::string jid = user + "@" + server + "/Device";
	return jid;
}

void LiveRelayServer::openClient()
{
	comn::IniProperties& props = AppProperties::getProperties();
	std::string password = props.getString("Xmpp.Password");
	std::string jid = getJid();

	CLog::info("connect to xmpp: %s\n", jid.c_str());

	m_client.reset(new Client(jid, password));

	m_client->registerStanzaExtension(new MediaControlStanza(0));

	m_client->registerConnectionListener(this);
	m_client->registerIqHandler(this, MediaControlStanza::EXTENSION_TYPE);
	m_client->connect(false);
}

void LiveRelayServer::closeClient()
{
	m_client.reset();
}

int LiveRelayServer::getTransportProtocol()
{
	int protocol = kProtocolUdp;
	comn::IniProperties& props = AppProperties::getProperties();
	std::string value = props.getString("Transport.Protocol", "Udp");
	comn::StringUtil::toLower(value);
	if (value == RtpMediaFormat::RTP_OVER_TCP)
	{
		protocol = kProtocolTcp;
	}
	return protocol;
}

bool LiveRelayServer::parseStreamName(const std::string& name, std::string& dev, std::string& chl)
{
    return comn::StringUtil::split(name, '/', dev, chl);
}

std::string LiveRelayServer::makeStreamName(const std::string& dev, const std::string& chl)
{
    if (chl.empty()) 
    {
        return dev;
    }
    return dev + '/' + chl;
}